#include <wx/file.h>
#include <wx/filename.h>

#include "MsnSession.h"
#include "MsnEvent.h"
#include "MsnConnection.h"
#include "MsnCommand.h"
#include "MsnContact.h"
#include "MsnMessage.h"
#include "MsnMessenger.h"

#include "FileTransferEntry.h"
#include "FileTransferManager.h"

#include "FileTransferConnection.h"

#include "P2PConnection.h"

#include "LogWriter.h"

#include "SessionConnection.h"

#include "ContactManager.h"

#include "UserConfig.h"

MessageHolder::MessageHolder(MsnMessage* msg) : message(msg), ackID(0)
{
	createTime = wxDateTime::GetTimeNow();

	
}

MessageHolder::~MessageHolder()
{
	wxDELETE(message);
}

int ID_TIMER_WAITING_MESSAGE_CHECKER = wxNewId();


MsnSession::MsnSession(MsnMessenger* mm) 
	: messenger(mm), transactionID(0), status(DISCONNECTED), connection(0), logWriter(0)
{
	DEBUG(_T("MsnSession"));

	ftManager = new FileTransferManager(this);
}

void MsnSession::InitLogWriter()
{
	wxString passport = _T("Unknown");
	if(contactList.size() > 0){
		passport = contactList[0];
	}
	logWriter = new LogWriter(messenger->GetHistoryDirectory(), passport);
}

MsnSession::~MsnSession()
{
	Close();

	ClearWaitingMessages();

	//TODO delete p2p  messages

	if(connection){
		connection->Destroy();
		wxDELETE(connection);
	}


	wxDELETE(ftManager);

	wxDELETE(logWriter);

	DEBUG(_T("~MsnSession"));
}

wxString MsnSession::GenerateCookie()
{
	return wxString::Format(_T("%ld"), wxDateTime::GetTimeNow());
}


wxString MsnSession::GeneratePseudoUUID()
{
	wxString result;

	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));

	result<<_T("-");

	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<_T("-");

	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<_T("-");

	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<_T("-");

	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));
	result<<wxString::Format(_T("%04x"), NumberUtil::RandInt(4369, 65450));

	return result;
}

bool MsnSession::IsAlive()
{	
	if(messenger->IsAlive()){
		wxString passport = GetContact(0);
		MsnContact* contact = messenger->GetContactManager()->GetContact(passport);
		return contact->IsOnline();
	}

	return false;
}

bool MsnSession::IsConnected()
{	
	return connection!=NULL && connection->IsConnected();
}

void MsnSession::Close()
{
	status = DISCONNECTED;

	if(connection != NULL){
		connection->Close();

	}	
}

void MsnSession::SetConnection(SessionConnection* conn)
{
	if(connection != NULL){
		DEBUG(_T("delete dead session connection"));
		wxDELETE(connection);
	}

	status = CONNECTING;
	connection = conn;
}

void MsnSession::AddContact(const wxString& contact)
{
	for(StringList::iterator it = contactList.begin();it != contactList.end(); ++it){
		if(contact.IsSameAs(*it)){
			return;
		}	
	}	

	contactList.push_back(contact);
}

void MsnSession::RemoveContact(const wxString& contact)
{
	for(StringList::iterator it = contactList.begin();it != contactList.end(); ++it){
		if(contact.IsSameAs(*it)){
			contactList.erase(it);
			return;
		}	
	}
}

wxString MsnSession::GetContact(int index)
{
	return contactList[index];
}


bool MsnSession::IsForContact(const wxString& contact)
{
	return contactList.size() == 1 && contact.IsSameAs(contactList[0]);
}

bool MsnSession::HasContact(const wxString& contact)
{
	for(StringList::iterator it = contactList.begin();it != contactList.end(); ++it){
		if(contact.IsSameAs(*it)) return true;
	}

	return false;	
}

wxString MsnSession::GetSessionTitle()
{
	wxString contacts;
	for(StringList::iterator it = contactList.begin();it != contactList.end(); ){
		wxString username = messenger->GetDisplayName(*it);
		contacts << username;
		++it;
		if(it != contactList.end()){
			contacts << _T(", ");
		}
	}

	return contacts;
}


void MsnSession::PostEvent(wxEvent& event)
{
	if(messenger != NULL){
		messenger->PostEvent(event);
	}
}

void MsnSession::OnConnectionClosed()
{
	DEBUG(_T("OnConnectionClosed"));		
	
	status = DISCONNECTED;
//	ResetConnection(NULL);
}

void MsnSession::InviteContact(const wxString& contact)
{
	DEBUG(_T("InviteContact: ")+contact);

	if(this->IsAlive()){
		connection->CallContact(contact);
	}else{
		this->AddContact(contact);
		//notify UI to change tab title
		wxCommandEvent event(EVT_MSN_SESSION_CONTACT_JOINED, -1);
		event.SetClientData(this);
		event.SetString(contact);
		PostEvent(event);
	}
}


//TODO handle those who are not in my list
void MsnSession::OnContactJoined(const wxString& passport)
{
	DEBUG(_T("OnContactJoined:") + passport);	

	AddContact(passport);

	//nofity UI
	wxCommandEvent event(EVT_MSN_SESSION_CONTACT_JOINED, -1);
	event.SetClientData(this);
    event.SetString(passport);
    PostEvent(event);

	status = CONNECTED;
	FlushWaitingMessages();
}

void MsnSession::OnContactLeft(const wxString& passport)
{
	DEBUG(_T("OnContactLeft:") + passport);	

	if(contactList.size() > 1){
		RemoveContact(passport);
		//notify UI
		wxCommandEvent event(EVT_MSN_SESSION_CONTACT_LEFT, -1);
		event.SetClientData(this);
		event.SetString(passport);
		PostEvent(event);
	}else{
		//the last contact left the session. we won't remove it and change the title, but just close the connection.
		connection->Close();
		status = DISCONNECTED;
	}
}

void MsnSession::OnContactListed(const wxString& passport)
{
	DEBUG(_T("OnSessionListed:") + passport);	

	if(!passport.IsEmpty()){
		AddContact(passport);
	}else{
		status = CONNECTED;
		//notify UI
		wxCommandEvent event(EVT_MSN_SESSION_CONTACT_LISTED, -1);
		event.SetClientData(this);
		event.SetString(passport);
		PostEvent(event);
	}
}

void MsnSession::OnInstantMessageReceived(InstantMessage* msg)
{
	DEBUG(_T("OnInstantMessageReceived"));

	bool underline = msg->IsUnderline();
    int style = wxFONTSTYLE_NORMAL;

	//int fontSize = wxNORMAL_FONT->GetPointSize();
	//use the config size for session view
	int fontSize = messenger->GetUserConfig()->GetMessageFont().GetPointSize();

    if(msg->IsItalic()){
        style = wxFONTSTYLE_ITALIC;
    }

    wxFontWeight weight = wxFONTWEIGHT_NORMAL;
	if(msg->IsBold()){
        weight = wxFONTWEIGHT_BOLD;
	}

    //bool strikeout = msg.IsStrikeout();

    wxColour color = wxColour(msg->GetColorRed(), msg->GetColorGreen(), msg->GetColorBlue());
    wxFont font = wxFont(fontSize, wxFONTFAMILY_DEFAULT, style, weight, underline, msg->GetFontName());

	//!!!the message item will be destroyed after it's displayed on session panel!!!
	InstantMessageItem* item = new InstantMessageItem;
	item->session = this;
	item->contact = msg->GetContact();
	item->content = msg->GetContent();
	item->color = color;
	item->font = font;

	if(messenger->GetUserConfig()->GetKeepHistory()){
		LogInstantMessage(msg);
	}

	wxCommandEvent event(EVT_MSN_INSTANT_MESSAGE_RECEIVED, -1);
    event.SetClientData(item);
    PostEvent(event);
}


void MsnSession::OnControlMessageReceived(MsnMessage* msg )
{
	DEBUG(_T("OnControlMessageReceived"));

	wxCommandEvent event(EVT_MSN_CONTROL_MESSAGE_RECEIVED, -1);
	event.SetString(msg->GetContact());
    PostEvent(event);
}

void MsnSession::OnACKReceived(int trID)
{
	DEBUG(_T("OnACKReceived :") + StringUtil::ToString(trID));

	MessageHolder* holder = RemoveWaitingMessage(trID);

	//sometime in HTTP transport, multi ack will be returned 
	if(holder == NULL){
		return;
	}

		//log history here
		MsnMessage* msg = holder->message;
		
		if(msg->IsKindOf(CLASSINFO(InstantMessage))){
			if(messenger->GetUserConfig()->GetKeepHistory()){
				LogInstantMessage((InstantMessage*)holder->message);	
			}
		}else if(msg->IsKindOf(CLASSINFO(P2PMessage))){
			P2PMessage* p2p = (P2PMessage*)msg;
			wxString sessionID = wxString::Format(_T("%d"), p2p->GetHeader()->sessionID);

			if(p2p->IsDataMessage()){
				//TODO update file transfer
				this->SendP2PFileDataMessage(sessionID);
			}

			//ACK for data preparation message
			if(p2p->IsDataPreparationMessage()){
				//send picture data
				SendP2PDisplayPictureData(sessionID);
			}

		}
		
	holder->ackID = -1;
	//delete holder;
}

void MsnSession::OnNAKReceived(int trID)
{
	DEBUG(_T("OnNAKReceived :") + StringUtil::ToString(trID));

	MessageHolder* holder = RemoveWaitingMessage(trID);

	if(holder == NULL){
		return;
	}

	holder->ackID = -2;
		
		//notify session panel
		if(holder->message->IsKindOf(CLASSINFO(InstantMessage))){
			InstantMessage* msg = (InstantMessage*)holder->message;
			wxCommandEvent event(EVT_MSN_INSTANT_MESSAGE_FAILED, -1);
			event.SetClientData(this);
			event.SetString(msg->GetContent());
			PostEvent(event);
		}

}


/*
void MsnSession::OnInvitationMessageReceived(InviteMessage* msg)
{
	wxString command = msg->GetProperty(_T("Invitation-Command"));
	if(command.IsSameAs(_T("INVITE"))){
		OnFileInviteReceived(msg);
	}else if(command.IsSameAs(_T("ACCEPT"))){		
		OnFileAcceptReceived(msg);
	}else if(command.IsSameAs(_T("CANCEL"))){
		OnFileCancelReceived(msg);      
	}	
}
*/

void MsnSession::OnP2PMessageReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PMessageReceived"));

	if(msg->GetHeader()->sessionID > 0 && msg->GetFooter() == 1 && msg->GetHeader()->flag == 0){
		OnP2PDataPreparationReceived(msg);
		return;
	}

	//pending message
	if(msg->GetHeader()->flag == 4){

	}
	//ack message
	else if(msg->GetHeader()->flag == 2){

		//TODO send data preparation message

	}else if(msg->GetHeader()->flag == 0x20){
		//display picture/custom emoticon data
		OnP2PDataReceived(msg);

	}else if(msg->GetHeader()->flag == 0x01000030){
		//data message
		OnP2PFileDataReceived(msg);

	}else if(msg->GetHeader()->messageLength < msg->GetHeader()->dataSize){	
		//it's a split message (not data)
		P2PMessage* completeMessage = MergeSplitP2PMessage(msg);
		if(completeMessage != NULL){
			OnP2PCompleteMessageReceived(completeMessage);
			delete completeMessage;
		}
	}else{
		msg->DecodeSlpMessage();
		OnP2PCompleteMessageReceived(msg);		
	}
}

P2PMessage* MsnSession::MergeSplitP2PMessage(P2PMessage* msg)
{
	DEBUG(_T("OnP2PSplitMessageReceived"));

	P2PMessage* splitMsg = p2pMessageMap[msg->GetHeader()->ID];
	if(splitMsg != NULL){
		//a subsequent message
		splitMsg->GetSlpBuffer().AppendData(msg->GetSlpBuffer().GetData(), msg->GetSlpBuffer().GetDataLen());

		//check if the multi msg is completed
		if(splitMsg->GetHeader()->dataSize == splitMsg->GetSlpBuffer().GetDataLen()){
			DEBUG(_T("split p2p message is complete!!!"));
			splitMsg->DecodeSlpMessage();
			p2pMessageMap.erase(splitMsg->GetHeader()->ID);
			return splitMsg;
		}
	}else{
		//it's the first message, will be copied.
		p2pMessageMap[msg->GetHeader()->ID] = new P2PMessage(*msg);
	}

	return NULL;
}

void MsnSession::OnP2PCompleteMessageReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PCompleteMessageReceived"));

	//normal p2p message
	if(msg->GetHeader()->flag == 0){
		SendP2PAckMessage(msg);
	}	

	SLPMessage* slpMsg = msg->GetSlpMessage();

	wxString method = slpMsg->GetMethod();
	wxString contentType = slpMsg->GetHeadProperty(_T("Content-Type"));

	if(method.Contains(_T("INVITE MSNMSGR"))){
		if(contentType.IsSameAs(_T("application/x-msnmsgr-sessionreqbody"))){

			wxString euf = slpMsg->GetProperty(_T("EUF-GUID"));

			if(euf.IsSameAs(MSN_P2P_EUF_GUID_FILE_TRANSFER)){
				OnP2PSessionInviteReceived(msg);
			}else if(euf.IsSameAs(MSN_P2P_EUF_GUID_DISPLAY_PICTURE)){
				OnP2PSessionInviteDisplayPictureReceived(msg);
			}else{
				DEBUG(_T("other p2p request"));
			}	

		}else if(contentType.IsSameAs(_T("application/x-msnmsgr-transreqbody"))){
			
			OnP2PTransferRequestReceived(msg);

		}else if(contentType.IsSameAs(_T("application/x-msnmsgr-transrespbody"))){
			OnP2PTransferAcceptReceived(msg);
		}

	}else if(method.Contains(_T("MSNSLP/1.0 200 OK"))){
		if(contentType.IsSameAs(_T("application/x-msnmsgr-sessionreqbody"))){
			OnP2PSessionAcceptReceived(msg);
		}else if(contentType.IsSameAs(_T("application/x-msnmsgr-transrespbody"))){
			OnP2PTransferAcceptReceived(msg);
		}
		
	}else if(method.Contains(_T("BYE MSNMSGR"))){
		OnP2PByeReceived(msg);
	}else if(method.Contains(_T("MSNSLP/1.0 603 Decline"))){
		OnP2PDeclineReceived(msg);
	}


	/*
ACK MSNMSGR:tuxitty2@hotmail.com MSNSLP/1.0
To: <msnmsgr:tuxitty2@hotmail.com>
From: <msnmsgr:tuxitty9@hotmail.com>
Via: MSNSLP/1.0/TLP ;branch={C745EFBD-A277-4630-9596-A4C3910516E3}
CSeq: 0 
Call-ID: {00000000-0000-0000-0000-000000000000}
Max-Forwards: 0
Content-Type: application/x-msnmsgr-transdestaddrupdate
Content-Length: 242

IPv4ExternalAddrsAndPorts: 210.13.80.206:1333
IPv4InternalAddrsAndPorts: 192.168.237.1:1254 192.168.153.1:1254 192.168.72.72:1254
IPv4External-Connecting-Port-End-Range: 1393
SessionID: 3315582
SChannelState: 0
Capabilities-Flags: 1

	*/
	
}


void MsnSession::OnP2PSessionInviteDisplayPictureReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PInviteDisplayPictureReceived"));

	DEBUG(msg->ToString());

	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString callID = slpMsg->GetHeadProperty(_T("Call-ID"));
	wxString context = slpMsg->GetProperty(_T("Context"));
	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));

	int appID = StringUtil::ToLong(slpMsg->GetProperty(_T("AppID")));

	char* buf = new char[context.size() + 1];
	StringUtil::Base64_Decode(WS2C(context), buf, context.size());

	wxString msnObject = wxString::FromAscii(buf);

	delete []buf;
	
	DEBUG(_T("\r\n ******************************************\r\n") 
			+ msnObject
			+ _T("\r\n ******************************************\r\n"));

	int fileSize = StringUtil::ToLong(StringUtil::SubString(msnObject, _T("Size=\""), _T("\"")));

	wxString fileName = StringUtil::SubString(msnObject, _T("Friendly=\""), _T("\""));

	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->isSender = true;
	item->SetID(sessionID);
	item->SetAppID(appID);
	item->SetCallID(callID);
	item->SetFileName(fileName);
	//item->SetFilePath(_T("c:\\222.jpg"));
	item->SetFilePath(messenger->GetDisplayPicture());
	item->SetFileSize(fileSize);

	item->SetMessage(new P2PMessage(*msg));

	ftManager->AddEntry(sessionID, item);

	DEBUG(messenger->GetDisplayPicture());

	//auto accept display picture
	SendP2PSessionAcceptMessage(msg);
}



void MsnSession::OnP2PSessionInviteReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PInviteReceived"));

	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString callID = slpMsg->GetHeadProperty(_T("Call-ID"));
	wxString context = slpMsg->GetProperty(_T("Context"));
	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));

	int appID = StringUtil::ToLong(slpMsg->GetProperty(_T("AppID")));

	char* buf = new char[context.size()];
	StringUtil::Base64_Decode(WS2C(context), buf, context.size());

		/*
			3e 02 00 00 02 00 00 00 80 00 00 00 00 00 00 00 
			01 00 00 00 70 00 72 00 6f 00 78 00 79 00 2e 00 
			74 00 78 00 74 00 00 00
		*/
	
	
	DEBUG(_T("\r\n ******************************************\r\n") 
			+ StringUtil::ToHexFormat((unsigned char*)buf, context.size())
			+ _T("\r\n ******************************************\r\n"));

	//always 574
	int size = StringUtil::ReadInt(buf, 0, 4);
	int fileSize = StringUtil::ReadInt(buf, 8, 4);

	int noPreview = StringUtil::ReadInt(buf, 16, 4);
		
	wxMBConvUTF16 utf16;
	wxString fileName = wxString(buf + 20,  utf16);

	wxString decodeContext;
	decodeContext << _T("size = ")<<size <<_T(" fileSize = ")<<fileSize<<_T(" fileName=")<<fileName<<_T(" noPreview=")<<noPreview;
	DEBUG(_T("context === "+decodeContext));

	if(!noPreview){
		//TODO preview data
	}

	delete []buf;


	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->isSender = false;
	item->SetID(sessionID);
	item->SetAppID(appID);
	item->SetCallID(callID);
	item->SetFileName(fileName);
	item->SetFileSize(fileSize);

	item->SetMessage(new P2PMessage(*msg));

	ftManager->AddEntry(sessionID, item);


	//notify UI for file transfer
	if(appID == MSN_P2P_APP_ID_FILE_TRANSFER){
		wxCommandEvent event(EVT_MSN_FILE_INVITE_RECEIVED, -1);
		event.SetClientData(item);
		PostEvent(event);
		return;
	}

	//auto accept display picture
	if(appID == MSN_P2P_APP_ID_DISPLAY_PICTURE){
		
	}

}

/*
MSNSLP/1.0 200 OK
To: <msnmsgr:tuxitty2@hotmail.com>
From: <msnmsgr:tuxitty9@hotmail.com>
Via: MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}
CSeq: 1 
Call-ID: {C5FF1FAA-981A-448B-9928-A64275E12632}
Max-Forwards: 0
Content-Type: application/x-msnmsgr-sessionreqbody
Content-Length: 63

SessionID: 123456
SChannelState: 0
Capabilities-Flags: 1
*/
void MsnSession::OnP2PSessionAcceptReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PAcceptReceived"));

	DEBUG(msg->ToString());


	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));

	//TODO send a transfer request
	FileTransferEntry* item = ftManager->GetEntry(sessionID);


	if(item->GetAppID() == MSN_P2P_APP_ID_DISPLAY_PICTURE){
	//	return;
	}


	item->SetMessage(new P2PMessage(*msg));


	//send P2P TransReq Message
	SLPMessage* slpResp = this->MakeSLPMessage(msg->GetSlpMessage(), _T("invite"), _T("application/x-msnmsgr-transreqbody"));

	slpResp->SetProperty(_T("Bridges"), _T("TCPv1"));
	slpResp->SetProperty(_T("NetID"), _T("-833614382"));

	if(messenger->IsBehindWall()){
		slpResp->SetProperty(_T("Conn-Type"), _T("IP-Restrict-NAT"));
		slpResp->SetProperty(_T("TCP-Conn-Type"), _T("IP-Restrict-NAT"));
	}else{
		slpResp->SetProperty(_T("Conn-Type"), _T("Direct-Connect"));
		slpResp->SetProperty(_T("TCP-Conn-Type"), _T("Direct-Connect"));
	}
	slpResp->SetProperty(_T("UPnPNat"), _T("false"));
	slpResp->SetProperty(_T("ICF"), _T("false"));
	//slpResp->SetProperty(_T("Nonce"), _T("{A175812B-337E-4C50-4280-AA2C176B5ACD}"));

	P2PMessage* p2pResp = MakeP2PMessage(msg->GetHeader()->sessionID, slpResp);

	SendMsnMessage(p2pResp);	
}



/*	
		INVITE MSNMSGR:tuxitty4@hotmail.com MSNSLP/1.0
		To: <msnmsgr:tuxitty4@hotmail.com>
		From: <msnmsgr:tuxitty9@hotmail.com>
		Via: MSNSLP/1.0/TLP ;branch={EAACDA73-8F27-49EA-8DC4-F45961472A58}
		CSeq: 0 
		Call-ID: {36E553C4-2CDB-4F45-8729-8F08F0CC0EAB}
		Max-Forwards: 0
		Content-Type: application/x-msnmsgr-transreqbody
		Content-Length: 269

		Bridges: TRUDPv1 TCPv1 SBBridge TURNv1
		NetID: -833614382
		Conn-Type: Port-Restrict-NAT
		TCP-Conn-Type: Port-Restrict-NAT
		UPnPNat: false
		ICF: false
		Hashed-Nonce: {A175812B-337E-4C50-4280-AA2C176B5ACD}
		SessionID: 4150504
		SChannelState: 0
		Capabilities-Flags: 1

*/

void MsnSession::OnP2PTransferRequestReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PTransferRequestReceived"));

	DEBUG(msg->ToString());

	SLPMessage* slpMsg = msg->GetSlpMessage();
	
	//send p2p trans resp message
	SLPMessage* slpResp = this->MakeSLPMessage(msg->GetSlpMessage(), _T("accept"), _T("application/x-msnmsgr-transrespbody"));
	
	wxString callID = slpMsg->GetHeadProperty(_T("Call-ID"));

	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));
	wxString bridges = slpMsg->GetProperty(_T("Bridges"));
	wxString connType = slpMsg->GetProperty(_T("Conn-Type"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);

	P2PConnection* worker = NULL;
	bool listening = false;

	//check the supported transport layers

	bool directConnectionAllowed = true;

	//disable direct connection for picture transfer
	if(item->GetAppID() == MSN_P2P_APP_ID_DISPLAY_PICTURE){
		directConnectionAllowed = false;
	}

	if(directConnectionAllowed && bridges.Contains(_T("TCPv1"))){
		
		//open the port for listening
		if(item->isSender){
			worker = new P2PFileSender(this, sessionID);
		}else{
			worker = new P2PFileReceiver(this, sessionID);
		}

		worker->SetCallID(callID);

		int openPort = worker->OpenServerSocket(2899);

		//port opened
		if(openPort != -1){
			listening = true;
			slpResp->SetProperty(_T("Bridge"), _T("TCPv1"));
			slpResp->SetProperty(_T("Listening"), _T("true"));

			if(messenger->IsBehindWall()){
				slpResp->SetProperty(_T("Conn-Type"), _T("IP-Restrict-NAT"));
				slpResp->SetProperty(_T("TCP-Conn-Type"), _T("IP-Restrict-NAT"));
			}else{
				slpResp->SetProperty(_T("Conn-Type"), _T("Direct-Connect"));
				slpResp->SetProperty(_T("TCP-Conn-Type"), _T("Direct-Connect"));
			}

			slpResp->SetProperty(_T("IPv4Internal-Addrs"), SystemUtil::GetLocalAddress());
			slpResp->SetProperty(_T("IPv4Internal-Port"),  StringUtil::ToString(openPort));

			slpResp->SetProperty(_T("Nonce"), _T("{A175812B-337E-4C50-4280-AA2C176B5ACD}"));
			//the generated UUID doesn't work here?????????????????????
			//slpResp->SetProperty(_T("Nonce"), GeneratePseudoUUID());

		}
	}

	//if not listening
	if(!listening){
		slpResp->SetProperty(_T("Bridge"), _T("TCPv1"));
		slpResp->SetProperty(_T("Listening"), _T("false"));
		slpResp->SetProperty(_T("Nonce"), _T("{00000000-0000-0000-0000-000000000000}"));
	}

	P2PMessage* p2pResp = MakeP2PMessage(msg->GetHeader()->sessionID, slpResp);
	SendMsnMessage(p2pResp);

	//attach the thread object
	item->SetConnection(worker);

	//start establishing the direct connection
	if(listening){
		bool accepted = worker->AcceptSocket();	
		//client socket connect
		if(accepted){
			worker->Start();
		}else{
			//do nothing	
		}
	}
}

/*
MSNSLP/1.0 200 OK
To: <msnmsgr:tuxitty2@hotmail.com>
From: <msnmsgr:tuxitty9@hotmail.com>
Via: MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}
CSeq: 3 
Call-ID: {C5FF1FAA-981A-448B-9928-A64275E12632}
Max-Forwards: 0
Content-Type: application/x-msnmsgr-transrespbody
Content-Length: 296

Bridge: TCPv1
Listening: true
Conn-Type: Direct-Connect
TCP-Conn-Type: Direct-Connect
Hashed-Nonce: {9FC043B3-00D8-F389-19FA-9CB536CE9894}
IPv4Internal-Addrs: 192.168.237.1 192.168.153.1 221.137.89.114
IPv4Internal-Port: 2899
SessionID: 123456
SChannelState: 0
Capabilities-Flags: 1

*/

/*
MSNSLP/1.0 200 OK
To: <msnmsgr:tuxitty2@hotmail.com>
From: <msnmsgr:tuxitty9@hotmail.com>
Via: MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}
CSeq: 3 
Call-ID: {C5FF1FAA-981A-448B-9928-A64275E12632}
Max-Forwards: 0
Content-Type: application/x-msnmsgr-transrespbody
Content-Length: 210

Bridge: TCPv1
Listening: false
Conn-Type: Port-Restrict-NAT
TCP-Conn-Type: Symmetric-NAT
Hashed-Nonce: {A34D9EBD-DEBC-884B-D6CA-CF227D80DBF2}
SessionID: 123456
SChannelState: 0
Capabilities-Flags: 1
*/

void MsnSession::OnP2PTransferAcceptReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PTransferAcceptReceived"));

	DEBUG(msg->ToString());

	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString callID = StringUtil::SubString(slpMsg->GetHeadProperty(_T("Call-ID")), _T("{"), _T("}"));

	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));
	bool listening = slpMsg->GetPropertyAsBool(_T("Listening"));
	wxString connType = slpMsg->GetProperty(_T("Conn-Type"));
	wxString tcpConnType = slpMsg->GetProperty(_T("TCP-Conn-Type"));

	//sometimes the sessionID field in TransResp message is empty???????
	//use callID instead to find the file transferring entry
	if(sessionID.IsEmpty()){
		
		//get the session id for use
		sessionID = ftManager->FindSessionID(callID);
	}


	FileTransferEntry* item = ftManager->GetEntry(sessionID);
	
	if(item == NULL){
		DEBUG(_T("File transfer entry not found!!!!!"));
			//TODO
		return;
	}

	bool directConnection = false;

	//for direct connection to send BYE message

	//switch
	bool directConnectionAllowed = true;

	//disable direct connection for picture transfer
	if(item->GetAppID() == MSN_P2P_APP_ID_DISPLAY_PICTURE){
		directConnectionAllowed = false;
	}

	if(directConnectionAllowed && listening){

		if(item->isSender){
			//send file
			wxString address = slpMsg->GetProperty(_T("IPv4Internal-Addrs"));
			int port = StringUtil::ToLong(slpMsg->GetProperty(_T("IPv4Internal-Port")));
			P2PFileSender* sender = new P2PFileSender(this, sessionID);

			sender->SetCallID(callID);

			//register the thread for deletion
			item->SetConnection(sender);

			bool connected = sender->ConnectSocket(address, port);

			if(connected){
				DEBUG(_T("sender connected with p2p direct"));

				directConnection = true;

				//for connection to send BYE message
				item->SetMessage(new P2PMessage(*msg));

				sender->Start();
			}

			GetFtManager()->NotifyStartedEvent(sessionID);

		}else{
			wxString address = slpMsg->GetProperty(_T("IPv4Internal-Addrs"));
			int port = StringUtil::ToLong(slpMsg->GetProperty(_T("IPv4Internal-Port")));

			P2PConnection* receiver = new P2PFileReceiver(this, sessionID);

			receiver->SetCallID(callID);

			//register the thread for deletion
			item->SetConnection(receiver);

			bool connected = receiver->ConnectSocket(address, port);

			if(connected){
				DEBUG(_T("receiver connected with p2p direct"));
				directConnection = true;
				receiver->Start();		
			}	
		}
	}else{
		
		//TODO create socketserver for listening

	}

	//if direct connection is not allowed, transfer the file via messages
	if(!directConnection)
	{
		//send data via P2P messages 
		if(item->isSender){
			
			if(item->GetAppID() == 12){
				//data preparation for display picture 
				SendP2PDataPreparationMessage(sessionID);
			}else{
				//data for file transfer 
				SendP2PFileDataMessage(sessionID);
			}
			
		}
	}

}

/*
MSNSLP/1.0 603 Decline
To: <msnmsgr:tuxitty@hotmail.com>
From: <msnmsgr:tuxitty9@hotmail.com>
Via: MSNSLP/1.0/TLP ;branch={637728d2-83c8-7cc4-1365-2d315f723fb0}
CSeq: 1 
Call-ID: {8e3256dd-9079-2dc0-28cb-6b7942b26f2d}
Max-Forwards: 0
Content-Type: application/x-msnmsgr-sessionreqbody
Content-Length: 62

SessionID: 23668
SChannelState: 0
Capabilities-Flags: 1
*/
void MsnSession::OnP2PDeclineReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PDeclineReceived"));

	DEBUG(msg->ToString());

	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);	

	//removed the item and notify UI
	ftManager->NotifyFinishedEvent(sessionID);

}


/*
		BYE MSNMSGR:tuxitty4@hotmail.com MSNSLP/1.0
		To: <msnmsgr:tuxitty4@hotmail.com>
		From: <msnmsgr:tuxitty9@hotmail.com>
		Via: MSNSLP/1.0/TLP ;branch={AAFE6284-6312-41E3-B96F-70E378F62884}
		CSeq: 0 
		Call-ID: {36E553C4-2CDB-4F45-8729-8F08F0CC0EAB}
		Max-Forwards: 0
		Content-Type: application/x-msnmsgr-sessionclosebody
		Content-Length: 64

		SessionID: 4150504
		SChannelState: 0
		Capabilities-Flags: 1
*/
void MsnSession::OnP2PByeReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PByeReceived"));

	DEBUG(msg->ToString());

	SLPMessage* slpMsg = msg->GetSlpMessage();
	wxString sessionID = slpMsg->GetProperty(_T("SessionID"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);

	//cancel the transferring item

	ftManager->NotifyFinishedEvent(sessionID);

}

void MsnSession::OnP2PDataPreparationReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PDataPreparationReceived"));
	SendP2PAckMessage(msg);	


}

void MsnSession::OnP2PDataReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PDataReceived"));

	wxString sessionID = wxString::Format(_T("%d"), msg->GetHeader()->sessionID);

	FileTransferEntry* item = ftManager->GetEntry(sessionID);
	if(item == NULL){
		//the entry might be cancelled
		return;
	}

	bool done = ftManager->WriteFileData(msg);

	int packetSize = msg->GetHeader()->messageLength;

	if(done){
		SendP2PAckMessage(msg);	
	}
}


void MsnSession::OnP2PFileDataReceived(P2PMessage* msg)
{
	DEBUG(_T("OnP2PFileDataReceived"));

	wxString sessionID = wxString::Format(_T("%d"), msg->GetHeader()->sessionID);

	FileTransferEntry* item = ftManager->GetEntry(sessionID);
	if(item == NULL){
		//the entry might be cancelled
		return;
	}

	bool done = ftManager->WriteFileData(msg);

	int packetSize = msg->GetHeader()->messageLength;

	ftManager->NotifyUpdatedEvent(sessionID, packetSize);

	if(done){
		SendP2PAckMessage(msg);	
		ftManager->NotifyFinishedEvent(sessionID);
	}
}


void MsnSession::SendP2PAckMessage(P2PMessage* msg)
{
	DEBUG(_T("SendP2PACKMessage"));

	P2PMessage* ack = new P2PMessage();
	
	ack->SetDestination(GetContact(0));

	ack->GetHeader()->sessionID = msg->GetHeader()->sessionID;
	ack->GetHeader()->ID = ~(msg->GetHeader()->ID);
	ack->GetHeader()->dataOffset = 0;
	ack->GetHeader()->dataSize = msg->GetHeader()->dataSize;
	ack->GetHeader()->messageLength = 0;
	ack->GetHeader()->flag = 0x02;
	ack->GetHeader()->ackID = msg->GetHeader()->ID;
	ack->GetHeader()->ackUID = msg->GetHeader()->ID;
	ack->GetHeader()->ackDataSize = msg->GetHeader()->dataSize;

	SendMsnMessage(ack);
}

/*
		INVITE MSNMSGR:tuxitty@hotmail.com MSNSLP/1.0
		To: <msnmsgr:tuxitty@hotmail.com>
		From: <msnmsgr:tuxitty9@hotmail.com>
		Via: MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}
		CSeq: 0 
		Call-ID: {C5FF1FAA-981A-448B-9928-A64275E12632}
		Max-Forwards: 0
		Content-Type: application/x-msnmsgr-sessionreqbody
		Content-Length: 903

		EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}
		SessionID: 2127908
		SChannelState: 0
		Capabilities-Flags: 1
		AppID: 2
		Context: PgIAAAIAAACAAAAAAAAAAAEAAABwAHIAbwB4AHkALgB0AHgA		
*/
void MsnSession::SendP2PSessionInviteMessage(const wxString& filePath)
{
	srand( (unsigned)time(0));

	//TODO generate session id
	wxString sessionID = StringUtil::ToString(NumberUtil::RandInt());

	wxString to = GetContact(0) ;
	wxString from = messenger->GetUserPassport();

	wxString callID = GeneratePseudoUUID();

	SLPMessage* slpMsg = 
		MakeSLPMessage( _T("invite"), 
						_T("application/x-msnmsgr-sessionreqbody"), 
						to, 
						from,
						//_T("MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}"),
						_T("MSNSLP/1.0/TLP ;branch={")+ GeneratePseudoUUID() +_T("}"),
						0,
						//_T("{C5FF1FAA-981A-448B-9928-A64275E12632}"));
						_T("{")+ callID +_T("}"));
						


	//indicator for file transfer
	slpMsg->SetProperty(_T("EUF-GUID"), _T("{5D3E02AB-6190-11D3-BBBB-00C04F795683}"));
	slpMsg->SetProperty(_T("AppID"), _T("2"));

	slpMsg->SetProperty(_T("SessionID"), sessionID);

	slpMsg->SetProperty(_T("SChannelState"), _T("0"));
	slpMsg->SetProperty(_T("Capabilities-Flags"), _T("1"));

	//set context

	wxFileName file(filePath);

	wxString fileName = file.GetFullName();
	int fileSize = file.GetSize().GetValue();

	char buf[590] = {0};
	StringUtil::WriteInt(buf, 0, 574);
	StringUtil::WriteInt(buf, 4, 2);
	StringUtil::WriteInt(buf, 8, fileSize);
	StringUtil::WriteInt(buf, 12, 0);
	StringUtil::WriteInt(buf, 16, 1); // no preview

	char* utf16Name = new char[fileName.Length()*2];
	StringUtil::ToUTF16(fileName, utf16Name);
	memcpy(buf + 20, utf16Name, fileName.Length()*2);
	delete utf16Name;

	//TODO find out the accurate size
	char encodeBuf[574  * 4] = {0};

	StringUtil::base64encode((unsigned char*)buf, 590, (unsigned char*)encodeBuf);
	wxString context = wxString::FromAscii(encodeBuf);
	slpMsg->SetProperty(_T("Context"), context);
		

	P2PMessage* p2p = MakeP2PMessage(0,  slpMsg);

	SendMsnMessage(p2p);

	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->SetID(sessionID);
	item->SetCallID(callID);
	item->SetFilePath(filePath);
	item->SetFileName(fileName);
	item->SetFileSize(fileSize);
	item->isSender = true;
	item->SetMessage(new P2PMessage(*p2p));

	ftManager->AddEntry(sessionID, item);

	wxCommandEvent event(EVT_MSN_FILE_INVITE_SENT, -1);
	event.SetClientData(item);
	PostEvent(event);
}

//
void MsnSession::SendP2PDisplayPictureRequest()
{
	srand( (unsigned)time(0));

	//TODO generate session id
	wxString sessionID = StringUtil::ToString(NumberUtil::RandInt());

	wxString to = GetContact(0) ;
	wxString from = messenger->GetUserPassport();

	wxString callID = GeneratePseudoUUID();

	SLPMessage* slpMsg = 
		MakeSLPMessage( _T("invite"), 
						_T("application/x-msnmsgr-sessionreqbody"), 
						to, 
						from,
						//_T("MSNSLP/1.0/TLP ;branch={CA9C2AC7-1107-4772-B8C8-8728C73A1A02}"),
						_T("MSNSLP/1.0/TLP ;branch={")+ GeneratePseudoUUID() +_T("}"),
						0,
						//_T("{C5FF1FAA-981A-448B-9928-A64275E12632}"));
						_T("{")+ callID +_T("}"));
						


	//indicator for display picture
	slpMsg->SetProperty(_T("EUF-GUID"), _T("{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"));
	slpMsg->SetProperty(_T("AppID"), _T("1"));

	slpMsg->SetProperty(_T("SessionID"), sessionID);

	//slpMsg->SetProperty(_T("SChannelState"), _T("0"));
	//slpMsg->SetProperty(_T("Capabilities-Flags"), _T("1"));

	//set context

	wxString msnObject = _T("<msnobj Creator=\"tuxitty9@hotmail.com\" Type=\"3\" SHA1D=\"KkGt9kR7ZH12hKE1yelLR1h7uwc=\" Size=\"2039\" Location=\"0\" Friendly=\"egB3AG8AMwBfADAAMgBfAFAAZQByAGYAZQBjAHQAXwBzAGkAZABlAAAA\"/>");

	
	//TODO find out the accurate size
	char encodeBuf[574  * 4] = {0};

	StringUtil::base64encode(WS2UC(msnObject), msnObject.Length(), (unsigned char*)encodeBuf);
	wxString context = wxString::FromAscii(encodeBuf);
	slpMsg->SetProperty(_T("Context"), context);
		
	P2PMessage* p2p = MakeP2PMessage(0,  slpMsg);

	SendMsnMessage(p2p);

	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->SetID(sessionID);
	item->SetCallID(callID);
	item->SetAppID(MSN_P2P_APP_ID_DISPLAY_PICTURE);
	item->SetFilePath(_T("c://xxx.jpg"));
	item->SetFileName(_T("xxx.jpg"));
	item->SetFileSize(100);
	item->isSender = false;
	item->SetMessage(new P2PMessage(*p2p));

	ftManager->AddEntry(sessionID, item);
}


P2PMessage* MsnSession::MakeP2PACKMessage(P2PMessage* msg)
{
	P2PMessage* ack = new P2PMessage();
	
	ack->SetDestination(GetContact(0));

	ack->GetHeader()->sessionID = msg->GetHeader()->sessionID;
	ack->GetHeader()->ID = ~(msg->GetHeader()->ID);
	ack->GetHeader()->dataOffset = 0;
	ack->GetHeader()->dataSize = msg->GetHeader()->dataSize;
	ack->GetHeader()->messageLength = 0;
	ack->GetHeader()->flag = 0x02;
	ack->GetHeader()->ackID = msg->GetHeader()->ID;
	ack->GetHeader()->ackUID = msg->GetHeader()->ID;
	ack->GetHeader()->ackDataSize = msg->GetHeader()->dataSize;

	return ack;
}


SLPMessage* MsnSession::MakeSLPMessage(const wxString& method, 
									   const wxString& contentType, 
									   const wxString& to, 
									   const wxString& from, 
									   const wxString& via, 
									   int cseq, 
									   const wxString& callID)
{
	DEBUG(_T("MakeSLPMessage: ") + method);

	SLPMessage* slpMsg = new SLPMessage();

	if(method.IsSameAs(_T("accept"))){
		slpMsg->SetMethod(_T("MSNSLP/1.0 200 OK"));
	}else if(method.IsSameAs(_T("decline"))){
		slpMsg->SetMethod(_T("MSNSLP/1.0 603 Decline"));
	}else if(method.IsSameAs(_T("invite"))){
		//TODO
		slpMsg->SetMethod(_T("INVITE MSNMSGR:") + to + _T(" MSNSLP/1.0"));
	}else if(method.IsSameAs(_T("bye"))){
		slpMsg->SetMethod(_T("BYE MSNMSGR:") + to + _T(" MSNSLP/1.0"));
	}else{
		return NULL;
	}

	slpMsg->SetHeadProperty(_T("To"), _T("<msnmsgr:") + to + _T(">"));
	slpMsg->SetHeadProperty(_T("From"), _T("<msnmsgr:") + from + _T(">"));
	slpMsg->SetHeadProperty(_T("Via"), via);
	slpMsg->SetHeadProperty(_T("CSeq"), wxString::Format(_T("%d"), cseq));

	slpMsg->SetHeadProperty(_T("Call-ID"), callID);
	slpMsg->SetHeadProperty(_T("Max-Forwards"), _T("0"));
	slpMsg->SetHeadProperty(_T("Content-Type"), contentType);

	return slpMsg;
}

SLPMessage* MsnSession::MakeSLPMessage(SLPMessage* reqMsg, const wxString& method, const wxString& contentType)
{	
	wxString to = this->GetContact(0);
	wxString from = this->GetMessenger()->GetUserPassport();
	wxString via = reqMsg->GetHeadProperty(_T("Via"));

	long cseq = StringUtil::ToLong(reqMsg->GetHeadProperty(_T("CSeq")))+1;

	wxString callID = reqMsg->GetHeadProperty(_T("Call-ID"));

	//slpMsg->SetHeadProperty(_T("Content-Type"),_T("application/x-msnmsgr-sessionreqbody"));

	SLPMessage* respMsg = this->MakeSLPMessage(method, contentType, to, from, via, cseq, callID);

	respMsg->SetProperty(_T("SessionID"), reqMsg->GetProperty(_T("SessionID")));

	respMsg->SetProperty(_T("SChannelState"), reqMsg->GetProperty(_T("SChannelState")));
	respMsg->SetProperty(_T("Capabilities-Flags"), reqMsg->GetProperty(_T("Capabilities-Flags")));

	return respMsg;
}



P2PMessage* MsnSession::MakeP2PMessage(int sessionID, SLPMessage* slpMsg)
{
	P2PMessage* p2p = new P2PMessage();

	p2p->SetDestination(GetContact(0));

	p2p->GetHeader()->sessionID = sessionID;
	p2p->GetHeader()->ID = NumberUtil::RandInt();
	p2p->GetHeader()->dataOffset = 0;

	int size = slpMsg->GetMessageLength();
	//no split
	p2p->GetHeader()->dataSize = size;
	p2p->GetHeader()->messageLength = size;

	p2p->GetHeader()->flag = 0;

	p2p->GetHeader()->ackID = NumberUtil::RandInt();

	p2p->GetHeader()->ackUID = 0;
	p2p->GetHeader()->ackDataSize = 0;


	p2p->SetSlpMessage(slpMsg);

	return p2p;
}

P2PMessage* MsnSession::MakeP2PDataMessage(int sessionID, int msgID, int dataOffset, int dataSize, int msgLen, int flag, char* buf)
{
	P2PMessage* p2p = new P2PMessage();

	p2p->SetDestination(GetContact(0));

	p2p->GetHeader()->sessionID = sessionID;
	//p2p->GetHeader()->ID = NumberUtil::RandInt();
	p2p->GetHeader()->ID = msgID;
	p2p->GetHeader()->dataOffset = dataOffset;

	p2p->GetHeader()->dataSize = dataSize;
	p2p->GetHeader()->messageLength = msgLen;

	p2p->GetHeader()->flag = flag;

	p2p->GetHeader()->ackID = NumberUtil::RandInt();

	p2p->GetHeader()->ackUID = 0;
	p2p->GetHeader()->ackDataSize = 0;

	p2p->GetSlpBuffer().AppendData(buf, msgLen);

	return p2p;
}

void MsnSession::SendP2PDataPreparationMessage(const wxString& sessionID)
{
	DEBUG(_T("SendP2PDataPreparationMessage"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);		

	//if the transfer is done or cancelled
	if(item == NULL || item->IsCancelled()){
		return;
	}

	char buf[4] = {0};	

	P2PMessage* dataMsg = MakeP2PDataMessage(StringUtil::ToLong(sessionID), item->GetMessageID() + 1, 0, 4, 4, 0, buf);

	dataMsg->SetFooter(1);
	
	SendMsnMessage(dataMsg);


}


void MsnSession::SendP2PDisplayPictureData(const wxString& sessionID)
{
	DEBUG(_T("SendP2PDisplayPicture"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);	
	
	while(!item->IsDone()){
		SendP2PFileDataMessage(sessionID);
	}

}


void MsnSession::SendP2PFileDataMessage(const wxString& sessionID)
{
	DEBUG(_T("SendP2PFileDataMessage"));

	FileTransferEntry* item = ftManager->GetEntry(sessionID);		

	//if the transfer is done or cancelled
	if(item == NULL || item->IsCancelled()){
		return;
	}

	char buf[MSN_P2P_DATA_PACKET_LENGTH] = {0};	
	int packetLen = 0;

	bool done = ftManager->ReadFileData(sessionID, buf, MSN_P2P_DATA_PACKET_LENGTH, packetLen);

	//no data read
	if(packetLen == 0){
		return;
	}


	int binaryFlag = 0;

	if(item->GetTransferType() == MSN_P2P_APP_ID_FILE_TRANSFER){
		binaryFlag = MSN_P2P_BINARY_FLAG_FILE_TRANSFER;
	}else if(item->GetTransferType() == MSN_P2P_APP_ID_DISPLAY_PICTURE){
		binaryFlag = MSN_P2P_BINARY_FLAG_DISPLAY_PICTURE;
	}else{
		DEBUG(_T("unsupported p2p transfer"));
		return;
	}

	P2PMessage* dataMsg = MakeP2PDataMessage(
		StringUtil::ToLong(sessionID), item->GetMessageID(), 
		item->GetFileOffset()-packetLen, item->GetFileSize(), 
		packetLen, binaryFlag, buf);
	
	SendMsnMessage(dataMsg);
	
	if(done){
		GetFtManager()->NotifyFinishedEvent(sessionID);
	}else{
		GetFtManager()->NotifyUpdatedEvent(sessionID, packetLen);
	}
}


void MsnSession::SendP2PSessionAcceptMessage(P2PMessage* reqMsg)
{
	DEBUG(_T("SendP2PSessionRespMessage"));

	//P2PMessage* resp = MakeP2PMessage(reqMsg, _T("accept"));

	SLPMessage* slpResp = this->MakeSLPMessage(reqMsg->GetSlpMessage(), _T("accept"), _T("application/x-msnmsgr-sessionreqbody"));

	P2PMessage* p2pResp = MakeP2PMessage(reqMsg->GetHeader()->sessionID, slpResp);

	SendMsnMessage(p2pResp);
}

void MsnSession::SendP2PSessionDeclineMessage(P2PMessage* reqMsg)
{
	DEBUG(_T("SendP2PSessionDeclineMessage"));

	SLPMessage* slpResp = this->MakeSLPMessage(reqMsg->GetSlpMessage(), _T("decline"), _T("application/x-msnmsgr-sessionreqbody"));

	P2PMessage* p2pResp = MakeP2PMessage(reqMsg->GetHeader()->sessionID, slpResp);

	SendMsnMessage(p2pResp);
}

void MsnSession::SendP2PByeMessage(P2PMessage* reqMsg)
{
	DEBUG(_T("SendP2PCancelMessage"));

	SLPMessage* slpResp = this->MakeSLPMessage(reqMsg->GetSlpMessage(), _T("bye"), _T("application/x-msnmsgr-sessionreqbody"));

	P2PMessage* p2pResp = MakeP2PMessage(reqMsg->GetHeader()->sessionID, slpResp);

	SendMsnMessage(p2pResp);
}













/*

void MsnSession::SendFileInviteMessage(const wxString& filePath)
{
	wxFileName file(filePath);

	///todo generate a cookie
    wxString cookie = GenerateCookie();

		//create a file transfer entry
	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->SetID(cookie);
	item->SetFilePath(filePath);
	item->SetFileName(file.GetFullName());
	item->SetFileSize(file.GetSize().ToULong());
	item->isSender = true;

	ftManager->AddEntry(cookie, item);

	InviteMessage* invite = new InviteMessage();
    invite->SetProperty(_T("Application-Name"), _T("File Transfer"));
    invite->SetProperty(_T("Application-GUID"), _T("{5D3E02AB-6190-11d3-BBBB-00C04F795683}"));
    invite->SetProperty(_T("Invitation-Command"), _T("INVITE"));
    invite->SetProperty(_T("Invitation-Cookie"), cookie);
    invite->SetProperty(_T("Application-File"), file.GetFullName());
    invite->SetProperty(_T("Application-FileSize"), file.GetSize().ToString());

	// check if we can work as server
	if(messenger->IsBehindWall()){
		invite->SetProperty(_T("Connectivity"), _T("N"));
		item->senderConnect = true;
	}

	SendMsnMessage(invite);

	wxCommandEvent event(EVT_MSN_FILE_INVITE_SENT, -1);
	event.SetClientData(item);
	PostEvent(event);
}

void MsnSession::OnFileInviteReceived(InviteMessage* msg)
{
	FileTransferEntry* item = new FileTransferEntry;
	item->SetSession(this);
	item->SetID(msg->GetInvitationCookie());
	item->SetFileName(msg->GetFile());
	item->fileSize = msg->GetFileSize();
	item->isSender = false;

	if(msg->GetConnectivity().IsSameAs(_T("N"))){
		//receiver as server
		item->senderConnect = true;
	}else{
		//receiver as client
		item->senderConnect = false;
	}

	ftManager->AddEntry(item->GetID(), item);

	wxCommandEvent event(EVT_MSN_FILE_INVITE_RECEIVED, -1);
	event.SetClientData(item);
	PostEvent(event);
}

void MsnSession::SendFileAcceptMessage(const wxString& cookie)
{
	//TODO check connection status
	FileTransferEntry* item = ftManager->GetEntry(cookie);

	InviteMessage* accept = new InviteMessage();
    accept->SetProperty(_T("Invitation-Command"),_T("ACCEPT"));
    accept->SetProperty(_T("Invitation-Cookie"), cookie);
    accept->SetProperty(_T("Launch-Application"),_T("FALSE"));
    accept->SetProperty(_T("Request-Data"),_T("IP-Address:"));

	//if the sender's connectivity is 'N'
	if(item->senderConnect){
		wxString authCookie = GenerateCookie();
		//open server socket
		FileTransferConnection* conn = new FtpFileReceiver(this, cookie, authCookie);
		conn->SetFilePath(item->GetFilePath());

		//work as server
		int port = conn->OpenServerSocket(MsnDef::MSN_FILE_TRANSFER_PORT);		
		conn->Open(wxEmptyString, 0);
		
		//TODO get gateway ip address
        wxString ip = SystemUtil::GetLocalAddress();
		accept->SetProperty(_T("Sender-Connect"), _T("TRUE"));
		accept->SetProperty(_T("AuthCookie"), authCookie);
		accept->SetProperty(_T("IP-Address"), messenger->GetExternalIP());
        accept->SetProperty(_T("Port"), wxString::Format(_T("%d"), port));

		if(messenger->IsBehindWall()){
			accept->SetProperty(_T("IP-Address-Internal"), messenger->GetInternalIP());
		}

		wxCommandEvent event(EVT_MSN_FILE_TRANSFER_STARTED, -1);
		event.SetString(cookie);
		event.SetClientData(conn);
		PostEvent(event);
	}

	SendMsnMessage(accept);
}

void MsnSession::OnFileAcceptReceived(InviteMessage* msg)
{
	wxString cookie = msg->GetInvitationCookie();
	FileTransferEntry* item = ftManager->GetEntry(cookie);

	FileTransferConnection* conn = NULL;

	if(item == NULL){
		//discard unexpected messages
		DEBUG(_T("discard unexpected accept message"));
		return;
	}


	if(!msg->GetProperty(_T("IP-Address")).IsEmpty()){


		DEBUG(_T("start FTP client connection "));

        wxString port = msg->GetProperty(_T("Port"));
		
        wxString authCookie = msg->GetProperty(_T("AuthCookie"));
			
		//TODO check which IP is accessable
		//get internal address first
		wxString address = msg->GetProperty(_T("IP-Address-Internal"));
		if(address.IsEmpty()){
			address = msg->GetProperty(_T("IP-Address"));
		}

		if(item->isSender){
			conn = new FtpFileSender(this, cookie, authCookie);
		}else{
			conn = new FtpFileReceiver(this, cookie, authCookie);
		}
	
		conn->SetFilePath(item->GetFilePath());			
		conn->Open(address, StringUtil::ToLong(port));
	}else{
        DEBUG(_T("start FTP server connection "));

		wxString authCookie = GenerateCookie();
		if(item->isSender){
			conn = new FtpFileSender(this, cookie, authCookie);
		}else{
			conn = new FtpFileReceiver(this, cookie, authCookie);
		}

		conn->SetFilePath(item->GetFilePath());

		//work as server
		int port = conn->OpenServerSocket(MsnDef::MSN_FILE_TRANSFER_PORT);		
		conn->Open(wxEmptyString, 0);
		
		//get gateway ip address
        wxString ip = SystemUtil::GetLocalAddress();

        InviteMessage* accept = new InviteMessage();
        accept->SetProperty(_T("Invitation-Command"), _T("ACCEPT"));
        accept->SetProperty(_T("Invitation-Cookie"), cookie);
        accept->SetProperty(_T("Launch-Application"), _T("FALSE"));
        accept->SetProperty(_T("Request-Data"), _T("IP-Address:"));
        accept->SetProperty(_T("AuthCookie"), authCookie);
		accept->SetProperty(_T("IP-Address"), messenger->GetExternalIP());
        accept->SetProperty(_T("Port"), wxString::Format(_T("%d"), port));

		if(messenger->IsBehindWall()){
			accept->SetProperty(_T("IP-Address-Internal"), messenger->GetInternalIP());
		}

		this->SendMsnMessage(accept);
	}
	wxCommandEvent event(EVT_MSN_FILE_TRANSFER_STARTED, -1);
	event.SetString(cookie);
	event.SetClientData(conn);
	PostEvent(event);
}

void MsnSession::SendFileDeclineMessage(const wxString& cookie)
{
	ftManager->DeleteEntry(cookie);

    InviteMessage* decline = new InviteMessage;
    decline->SetProperty(_T("Invitation-Command"),_T("CANCEL"));
    decline->SetProperty(_T("Invitation-Cookie"), cookie);
    decline->SetProperty(_T("Cancel-Code"),_T("REJECT"));

	SendMsnMessage(decline);
}

void MsnSession::SendFileCancelMessage(const wxString& cookie)
{
    InviteMessage* cancel = new InviteMessage;
    cancel->SetProperty(_T("Invitation-Command"),_T("CANCEL"));
    cancel->SetProperty(_T("Invitation-Cookie"), cookie);
    cancel->SetProperty(_T("Cancel-Code"),_T("TIMEOUT"));

	SendMsnMessage(cancel);
}


void MsnSession::OnFileCancelReceived(InviteMessage* msg)
{
	wxString cookie = msg->GetInvitationCookie();
	
	ftManager->DeleteEntry(cookie);

	wxCommandEvent event(EVT_MSN_FILE_TRANSFER_CANCELLED, -1);
	event.SetString(cookie);
	event.SetClientData(this);
			
	PostEvent(event);	
}
*/

void MsnSession::SendInstantMessage(const wxString& content)
{
	InstantMessage* msg = MakeInstantMessage(content);

	SendMsnMessage(msg);
}

void MsnSession::SendMsnMessage(MsnMessage* msg)
{
	MessageHolder* holder = new MessageHolder(msg);
	AddWaitingMessage(holder);

	if(status == CONNECTED){	
		SendMessageHolder(holder);	
		return;
	}

	if(status == DISCONNECTED){
		DEBUG(_T("start connecting"));
		status = CONNECTING;
		transactionID = messenger->StartSession();
		return;
	}

	if(status == CONNECTING){
		DEBUG(_T("do nothing when session is connecting"));
	}	
}

void MsnSession::SendInstantMessage(InstantMessage* msg)
{
	SendMsnMessage(msg);
}

void MsnSession::SendControlMessage()
{
	if(status == CONNECTED){
		ControlMessage msg;
		connection->SendMessage(msg);
	}
}

InstantMessage* MsnSession::MakeInstantMessage(const wxString& content)
{
	InstantMessage* msg = new InstantMessage();
	msg->SetContact(messenger->GetUserPassport());
    msg->SetContent(content);

    wxColour colour = messenger->GetUserConfig()->GetMessageColor();
    wxFont font = messenger->GetUserConfig()->GetMessageFont();
    msg->SetColorBlue(colour.Blue());
    msg->SetColorRed(colour.Red());
    msg->SetColorGreen(colour.Green());
    msg->SetFontName(font.GetFaceName());

	return msg;
}


void MsnSession::AddWaitingMessage(MessageHolder* holder)
{
	wxMutexLocker lock(waitingMessageMutex);

	waitingMessageList.push_back(holder);	
}

MessageHolder* MsnSession::RemoveWaitingMessage(int ackID)
{
	wxMutexLocker lock(waitingMessageMutex);

	for(MessageList::iterator it = waitingMessageList.begin(); it != waitingMessageList.end(); ++it){
		MessageHolder* holder = *it;
		if(holder->ackID == ackID){
			//waitingMessageList.erase(it);
			return holder;
		}
	}
	return NULL;
}


void MsnSession::FlushWaitingMessages()
{
	if(waitingMessageList.empty()) return;

	DEBUG(_T("Flush waiting messages"));

	wxMutexLocker lock(waitingMessageMutex);

	for(MessageList::iterator it = waitingMessageList.begin();it != waitingMessageList.end(); ++it){
        MessageHolder* holder = *it;
		
		if(holder->ackID == 0){
			MsnMessage* msg = holder->message;
			//if(msg->IsKindOf(CLASSINFO(InstantMessage))){
			SendMessageHolder(holder);

			//holder->createTime = 0;
		}
	}
}

void MsnSession::CheckWaitingMessages()
{
	if(waitingMessageList.empty()) return;

	DEBUG(_T("Check waiting messages"));

	wxMutexLocker lock(waitingMessageMutex);	

	time_t now = wxDateTime::GetTimeNow();

	for(MessageList::iterator it = waitingMessageList.begin();it != waitingMessageList.end(); ){
        MessageHolder* holder = *it;
		
		//failed or sent out
		if(holder->ackID < 0){
			it = waitingMessageList.erase(it);
			delete holder;
			continue;
		}

		//time out
		if(now - holder->createTime > 60){
			//TODO timeout notify UI	
			DEBUG(_T("message failed!!!!!!!!!!!!!!!!!!!!!!"));

			InstantMessage* msg = (InstantMessage*)holder->message;
			wxCommandEvent event(EVT_MSN_INSTANT_MESSAGE_FAILED, -1);
			event.SetClientData(this);
			event.SetString(msg->GetContent());
			PostEvent(event);

			//iterator will point to next
			it = waitingMessageList.erase(it);
			delete holder;
			continue;
		}
			
		++it;
	}
}

void MsnSession::ClearWaitingMessages()
{
	wxMutexLocker lock(waitingMessageMutex);

	for(MessageList::iterator it = waitingMessageList.begin();it != waitingMessageList.end(); ){
        MessageHolder* holder = *it;
		it = waitingMessageList.erase(it);
		delete holder;
	}
}

void MsnSession::SendMessageHolder(MessageHolder* holder)
{
	//int ackID = connection->SendMessage(*(holder->message), );
	//holder->ackID = ackID;

	connection->SendMessage(*(holder->message), holder->ackID);
	
}

void MsnSession::LogInstantMessage(InstantMessage* msg)
{
	if(logWriter == NULL){
		InitLogWriter();
	}

	wxString username = messenger->GetDisplayName(msg->GetContact());
	//bool newline = messenger->GetUserConfig()->GetShowNewLine();
	logWriter->Log(username, msg->GetContent());
}
